#pragma once

#include "../widgets.h"
#include "basechart_layer.h"
#include "basechart_layout.h"
#include "basechart_lineending.h"
#include "basechart_selection.h"

class BaseChartCustomPlot;
class BaseChartAxisPainterPrivate;
class BaseChartAbstractItem;
class BaseChartGraph;
class S_WIDGETS_EXPORT BaseChartAxisTicker {
  Q_GADGET
 public:
  /*!
    Defines the strategies that the axis ticker may follow when choosing the
    size of the tick step.

    \see setTickStepStrategy
  */
  enum TickStepStrategy {
    tssReadability  ///< A nicely readable tick step is prioritized over
                    ///< matching the requested number of ticks (see \ref
                    ///< setTickCount)
    ,
    tssMeetTickCount  ///< Less readable tick steps are allowed which in turn
                      ///< facilitates getting closer to the requested tick
                      ///< count
  };
  Q_ENUMS(TickStepStrategy)

  BaseChartAxisTicker();
  virtual ~BaseChartAxisTicker();

  // getters:
  TickStepStrategy tickStepStrategy() const { return mTickStepStrategy; }
  int tickCount() const { return mTickCount; }
  double tickOrigin() const { return mTickOrigin; }

  // setters:
  void setTickStepStrategy(TickStepStrategy strategy);
  void setTickCount(int count);
  void setTickOrigin(double origin);

  // introduced virtual methods:
  virtual void generate(const BaseChartRange& range, const QLocale& locale,
                        QChar formatChar, int precision, QVector<double>& ticks,
                        QVector<double>* subTicks,
                        QVector<QString>* tickLabels);

 protected:
  // property members:
  TickStepStrategy mTickStepStrategy;
  int mTickCount;
  double mTickOrigin;

  // introduced virtual methods:
  virtual double getTickStep(const BaseChartRange& range);
  virtual int getSubTickCount(double tickStep);
  virtual QString getTickLabel(double tick, const QLocale& locale,
                               QChar formatChar, int precision);
  virtual QVector<double> createTickVector(double tickStep,
                                           const BaseChartRange& range);
  virtual QVector<double> createSubTickVector(int subTickCount,
                                              const QVector<double>& ticks);
  virtual QVector<QString> createLabelVector(const QVector<double>& ticks,
                                             const QLocale& locale,
                                             QChar formatChar, int precision);

  // non-virtual methods:
  void trimTicks(const BaseChartRange& range, QVector<double>& ticks,
                 bool keepOneOutlier) const;
  double pickClosest(double target, const QVector<double>& candidates) const;
  double getMantissa(double input, double* magnitude = nullptr) const;
  double cleanMantissa(double input) const;

 private:
  Q_DISABLE_COPY(BaseChartAxisTicker)
};

class S_WIDGETS_EXPORT BaseChartAxisTickerDateTime
    : public BaseChartAxisTicker {
 public:
  BaseChartAxisTickerDateTime();

  // getters:
  QString dateTimeFormat() const { return mDateTimeFormat; }
  Qt::TimeSpec dateTimeSpec() const { return mDateTimeSpec; }
  QTimeZone timeZone() const { return mTimeZone; }

  // setters:
  void setDateTimeFormat(const QString& format);
  void setDateTimeSpec(Qt::TimeSpec spec);
  void setTimeZone(const QTimeZone& zone);
  void setTickOrigin(
      double origin);  // hides base class method but calls baseclass
                       // implementation ("using" throws off IDEs and doxygen)
  void setTickOrigin(const QDateTime& origin);

  // static methods:
  static QDateTime keyToDateTime(double key);
  static double dateTimeToKey(const QDateTime& dateTime);
  static double dateTimeToKey(const QDate& date,
                              Qt::TimeSpec timeSpec = Qt::LocalTime);

 protected:
  // property members:
  QString mDateTimeFormat;
  Qt::TimeSpec mDateTimeSpec;
  QTimeZone mTimeZone;

  // non-property members:
  enum DateStrategy {
    dsNone,
    dsUniformTimeInDay,
    dsUniformDayInMonth
  } mDateStrategy;

  // reimplemented virtual methods:
  virtual double getTickStep(const BaseChartRange& range) Q_DECL_OVERRIDE;
  virtual int getSubTickCount(double tickStep) Q_DECL_OVERRIDE;
  virtual QString getTickLabel(double tick, const QLocale& locale,
                               QChar formatChar, int precision) Q_DECL_OVERRIDE;
  virtual QVector<double> createTickVector(
      double tickStep, const BaseChartRange& range) Q_DECL_OVERRIDE;
};

class S_WIDGETS_EXPORT BaseChartAxisTickerTime : public BaseChartAxisTicker {
  Q_GADGET
 public:
  /*!
    Defines the logical units in which fractions of time spans can be expressed.

    \see setFieldWidth, setTimeFormat
  */
  enum TimeUnit {
    tuMilliseconds  ///< Milliseconds, one thousandth of a second (%%z in \ref
                    ///< setTimeFormat)
    ,
    tuSeconds  ///< Seconds (%%s in \ref setTimeFormat)
    ,
    tuMinutes  ///< Minutes (%%m in \ref setTimeFormat)
    ,
    tuHours  ///< Hours (%%h in \ref setTimeFormat)
    ,
    tuDays  ///< Days (%%d in \ref setTimeFormat)
  };
  Q_ENUMS(TimeUnit)

  BaseChartAxisTickerTime();

  // getters:
  QString timeFormat() const { return mTimeFormat; }
  int fieldWidth(TimeUnit unit) const { return mFieldWidth.value(unit); }

  // setters:
  void setTimeFormat(const QString& format);
  void setFieldWidth(TimeUnit unit, int width);

 protected:
  // property members:
  QString mTimeFormat;
  QHash<TimeUnit, int> mFieldWidth;

  // non-property members:
  TimeUnit mSmallestUnit, mBiggestUnit;
  QHash<TimeUnit, QString> mFormatPattern;

  // reimplemented virtual methods:
  virtual double getTickStep(const BaseChartRange& range) Q_DECL_OVERRIDE;
  virtual int getSubTickCount(double tickStep) Q_DECL_OVERRIDE;
  virtual QString getTickLabel(double tick, const QLocale& locale,
                               QChar formatChar, int precision) Q_DECL_OVERRIDE;

  // non-virtual methods:
  void replaceUnit(QString& text, TimeUnit unit, int value) const;
};

class S_WIDGETS_EXPORT BaseChartAxisTickerFixed : public BaseChartAxisTicker {
  Q_GADGET
 public:
  /*!
    Defines how the axis ticker may modify the specified tick step (\ref
    setTickStep) in order to control the number of ticks in the axis range.

    \see setScaleStrategy
  */
  enum ScaleStrategy {
    ssNone  ///< Modifications are not allowed, the specified tick step is
            ///< absolutely fixed. This might cause a high tick density and
            ///< overlapping labels if the axis range is zoomed out.
    ,
    ssMultiples  ///< An integer multiple of the specified tick step is allowed.
                 ///< The used factor follows the base class properties of \ref
                 ///< setTickStepStrategy and \ref setTickCount.
    ,
    ssPowers  ///< An integer power of the specified tick step is allowed.
  };
  Q_ENUMS(ScaleStrategy)

  BaseChartAxisTickerFixed();

  // getters:
  double tickStep() const { return mTickStep; }
  ScaleStrategy scaleStrategy() const { return mScaleStrategy; }

  // setters:
  void setTickStep(double step);
  void setScaleStrategy(ScaleStrategy strategy);

 protected:
  // property members:
  double mTickStep;
  ScaleStrategy mScaleStrategy;

  // reimplemented virtual methods:
  virtual double getTickStep(const BaseChartRange& range) Q_DECL_OVERRIDE;
};

class S_WIDGETS_EXPORT BaseChartAxisTickerText : public BaseChartAxisTicker {
 public:
  BaseChartAxisTickerText();

  // getters:
  QMap<double, QString>& ticks() { return mTicks; }
  int subTickCount() const { return mSubTickCount; }

  // setters:
  void setTicks(const QMap<double, QString>& ticks);
  void setTicks(const QVector<double>& positions,
                const QVector<QString>& labels);
  void setSubTickCount(int subTicks);

  // non-virtual methods:
  void clear();
  void addTick(double position, const QString& label);
  void addTicks(const QMap<double, QString>& ticks);
  void addTicks(const QVector<double>& positions,
                const QVector<QString>& labels);

 protected:
  // property members:
  QMap<double, QString> mTicks;
  int mSubTickCount;

  // reimplemented virtual methods:
  virtual double getTickStep(const BaseChartRange& range) Q_DECL_OVERRIDE;
  virtual int getSubTickCount(double tickStep) Q_DECL_OVERRIDE;
  virtual QString getTickLabel(double tick, const QLocale& locale,
                               QChar formatChar, int precision) Q_DECL_OVERRIDE;
  virtual QVector<double> createTickVector(
      double tickStep, const BaseChartRange& range) Q_DECL_OVERRIDE;
};

class S_WIDGETS_EXPORT BaseChartAxisTickerPi : public BaseChartAxisTicker {
  Q_GADGET
 public:
  /*!
    Defines how fractions should be displayed in tick labels.

    \see setFractionStyle
  */
  enum FractionStyle {
    fsFloatingPoint  ///< Fractions are displayed as regular decimal floating
                     ///< point numbers, e.g. "0.25" or "0.125".
    ,
    fsAsciiFractions  ///< Fractions are written as rationals using ASCII
                      ///< characters only, e.g. "1/4" or "1/8"
    ,
    fsUnicodeFractions  ///< Fractions are written using sub- and superscript
                        ///< UTF-8 digits and the fraction symbol.
  };
  Q_ENUMS(FractionStyle)

  BaseChartAxisTickerPi();

  // getters:
  QString piSymbol() const { return mPiSymbol; }
  double piValue() const { return mPiValue; }
  bool periodicity() const { return mPeriodicity; }
  FractionStyle fractionStyle() const { return mFractionStyle; }

  // setters:
  void setPiSymbol(QString symbol);
  void setPiValue(double pi);
  void setPeriodicity(int multiplesOfPi);
  void setFractionStyle(FractionStyle style);

 protected:
  // property members:
  QString mPiSymbol;
  double mPiValue;
  int mPeriodicity;
  FractionStyle mFractionStyle;

  // non-property members:
  double mPiTickStep;  // size of one tick step in units of mPiValue

  // reimplemented virtual methods:
  virtual double getTickStep(const BaseChartRange& range) Q_DECL_OVERRIDE;
  virtual int getSubTickCount(double tickStep) Q_DECL_OVERRIDE;
  virtual QString getTickLabel(double tick, const QLocale& locale,
                               QChar formatChar, int precision) Q_DECL_OVERRIDE;

  // non-virtual methods:
  void simplifyFraction(int& numerator, int& denominator) const;
  QString fractionToString(int numerator, int denominator) const;
  QString unicodeFraction(int numerator, int denominator) const;
  QString unicodeSuperscript(int number) const;
  QString unicodeSubscript(int number) const;
};

class S_WIDGETS_EXPORT BaseChartAxisTickerLog : public BaseChartAxisTicker {
 public:
  BaseChartAxisTickerLog();

  // getters:
  double logBase() const { return mLogBase; }
  int subTickCount() const { return mSubTickCount; }

  // setters:
  void setLogBase(double base);
  void setSubTickCount(int subTicks);

 protected:
  // property members:
  double mLogBase;
  int mSubTickCount;

  // non-property members:
  double mLogBaseLnInv;

  // reimplemented virtual methods:
  virtual int getSubTickCount(double tickStep) Q_DECL_OVERRIDE;
  virtual QVector<double> createTickVector(
      double tickStep, const BaseChartRange& range) Q_DECL_OVERRIDE;
};

class S_WIDGETS_EXPORT BaseChartCustomAxisGrid {
 public:
  virtual bool drawGridXLines(BaseChartPainter* painter) = 0;
  virtual bool drawGridYLines(BaseChartPainter* painter) = 0;
};

class S_WIDGETS_EXPORT QBAxisGrid : public BaseChartLayerable {
  Q_OBJECT
  /// \cond INCLUDE_QPROPERTIES
  Q_PROPERTY(bool subGridVisible READ subGridVisible WRITE setSubGridVisible)
  Q_PROPERTY(bool antialiasedSubGrid READ antialiasedSubGrid WRITE
                 setAntialiasedSubGrid)
  Q_PROPERTY(bool antialiasedZeroLine READ antialiasedZeroLine WRITE
                 setAntialiasedZeroLine)
  Q_PROPERTY(QPen pen READ pen WRITE setPen)
  Q_PROPERTY(QPen subGridPen READ subGridPen WRITE setSubGridPen)
  Q_PROPERTY(QPen zeroLinePen READ zeroLinePen WRITE setZeroLinePen)
  /// \endcond
 public:
  explicit QBAxisGrid(BaseChartAxis* parentAxis);

  // getters:
  bool subGridVisible() const { return mSubGridVisible; }
  bool antialiasedSubGrid() const { return mAntialiasedSubGrid; }
  bool antialiasedZeroLine() const { return mAntialiasedZeroLine; }
  QPen pen() const { return mPen; }
  QPen subGridPen() const { return mSubGridPen; }
  QPen zeroLinePen() const { return mZeroLinePen; }

  // setters:
  void setSubGridVisible(bool visible);
  void setAntialiasedSubGrid(bool enabled);
  void setAntialiasedZeroLine(bool enabled);
  void setPen(const QPen& pen);
  void setSubGridPen(const QPen& pen);
  void setZeroLinePen(const QPen& pen);

  void setCustomAxisGrid(BaseChartCustomAxisGrid* grid);

 protected:
  // property members:
  bool mSubGridVisible;
  bool mAntialiasedSubGrid, mAntialiasedZeroLine;
  QPen mPen, mSubGridPen, mZeroLinePen;

  // non-property members:
  BaseChartAxis* mParentAxis;
  BaseChartCustomAxisGrid* mCustomAxisGrid;

  // reimplemented virtual methods:
  virtual void applyDefaultAntialiasingHint(BaseChartPainter* painter) const
      Q_DECL_OVERRIDE;
  virtual void draw(BaseChartPainter* painter) Q_DECL_OVERRIDE;

  // non-virtual methods:
  void drawGridLines(BaseChartPainter* painter) const;
  void drawSubGridLines(BaseChartPainter* painter) const;

  friend class BaseChartAxis;
};

class BaseChartAxisRect;
class S_WIDGETS_EXPORT BaseChartAxis : public BaseChartLayerable {
  Q_OBJECT
  /// \cond INCLUDE_QPROPERTIES
  Q_PROPERTY(AxisType axisType READ axisType)
  Q_PROPERTY(BaseChartAxisRect* axisRect READ axisRect)
  Q_PROPERTY(ScaleType scaleType READ scaleType WRITE setScaleType NOTIFY
                 scaleTypeChanged)
  Q_PROPERTY(BaseChartRange range READ range WRITE setRange NOTIFY rangeChanged)
  Q_PROPERTY(bool rangeReversed READ rangeReversed WRITE setRangeReversed)
  Q_PROPERTY(
      QSharedPointer<BaseChartAxisTicker> ticker READ ticker WRITE setTicker)
  Q_PROPERTY(bool ticks READ ticks WRITE setTicks)
  Q_PROPERTY(bool tickLabels READ tickLabels WRITE setTickLabels)
  Q_PROPERTY(
      int tickLabelPadding READ tickLabelPadding WRITE setTickLabelPadding)
  Q_PROPERTY(QFont tickLabelFont READ tickLabelFont WRITE setTickLabelFont)
  Q_PROPERTY(QColor tickLabelColor READ tickLabelColor WRITE setTickLabelColor)
  Q_PROPERTY(double tickLabelRotation READ tickLabelRotation WRITE
                 setTickLabelRotation)
  Q_PROPERTY(LabelSide tickLabelSide READ tickLabelSide WRITE setTickLabelSide)
  Q_PROPERTY(QString numberFormat READ numberFormat WRITE setNumberFormat)
  Q_PROPERTY(int numberPrecision READ numberPrecision WRITE setNumberPrecision)
  Q_PROPERTY(QVector<double> tickVector READ tickVector)
  Q_PROPERTY(QVector<QString> tickVectorLabels READ tickVectorLabels)
  Q_PROPERTY(int tickLengthIn READ tickLengthIn WRITE setTickLengthIn)
  Q_PROPERTY(int tickLengthOut READ tickLengthOut WRITE setTickLengthOut)
  Q_PROPERTY(bool subTicks READ subTicks WRITE setSubTicks)
  Q_PROPERTY(int subTickLengthIn READ subTickLengthIn WRITE setSubTickLengthIn)
  Q_PROPERTY(
      int subTickLengthOut READ subTickLengthOut WRITE setSubTickLengthOut)
  Q_PROPERTY(QPen basePen READ basePen WRITE setBasePen)
  Q_PROPERTY(QPen tickPen READ tickPen WRITE setTickPen)
  Q_PROPERTY(QPen subTickPen READ subTickPen WRITE setSubTickPen)
  Q_PROPERTY(QFont labelFont READ labelFont WRITE setLabelFont)
  Q_PROPERTY(QColor labelColor READ labelColor WRITE setLabelColor)
  Q_PROPERTY(QString label READ label WRITE setLabel)
  Q_PROPERTY(int labelPadding READ labelPadding WRITE setLabelPadding)
  Q_PROPERTY(int padding READ padding WRITE setPadding)
  Q_PROPERTY(int offset READ offset WRITE setOffset)
  Q_PROPERTY(SelectableParts selectedParts READ selectedParts WRITE
                 setSelectedParts NOTIFY selectionChanged)
  Q_PROPERTY(SelectableParts selectableParts READ selectableParts WRITE
                 setSelectableParts NOTIFY selectableChanged)
  Q_PROPERTY(QFont selectedTickLabelFont READ selectedTickLabelFont WRITE
                 setSelectedTickLabelFont)
  Q_PROPERTY(
      QFont selectedLabelFont READ selectedLabelFont WRITE setSelectedLabelFont)
  Q_PROPERTY(QColor selectedTickLabelColor READ selectedTickLabelColor WRITE
                 setSelectedTickLabelColor)
  Q_PROPERTY(QColor selectedLabelColor READ selectedLabelColor WRITE
                 setSelectedLabelColor)
  Q_PROPERTY(QPen selectedBasePen READ selectedBasePen WRITE setSelectedBasePen)
  Q_PROPERTY(QPen selectedTickPen READ selectedTickPen WRITE setSelectedTickPen)
  Q_PROPERTY(QPen selectedSubTickPen READ selectedSubTickPen WRITE
                 setSelectedSubTickPen)
  Q_PROPERTY(
      BaseChartLineEnding lowerEnding READ lowerEnding WRITE setLowerEnding)
  Q_PROPERTY(
      BaseChartLineEnding upperEnding READ upperEnding WRITE setUpperEnding)
  Q_PROPERTY(QBAxisGrid* grid READ grid)
  /// \endcond
 public:
  /*!
    Defines at which side of the axis rect the axis will appear. This also
    affects how the tick marks are drawn, on which side the labels are placed
    etc.
  */
  enum AxisType {
    atLeft = 0x01  ///< <tt>0x01</tt> Axis is vertical and on the left side of
                   ///< the axis rect
    ,
    atRight = 0x02  ///< <tt>0x02</tt> Axis is vertical and on the right side of
                    ///< the axis rect
    ,
    atTop = 0x04  ///< <tt>0x04</tt> Axis is horizontal and on the top side of
                  ///< the axis rect
    ,
    atBottom = 0x08  ///< <tt>0x08</tt> Axis is horizontal and on the bottom
                     ///< side of the axis rect
  };
  Q_ENUMS(AxisType)
  Q_FLAGS(AxisTypes)
  Q_DECLARE_FLAGS(AxisTypes, AxisType)
  /*!
      Defines on which side of the axis the tick labels (numbers) shall appear.

      \see setTickLabelSide
  */
  enum LabelSide {
    lsInside  ///< Tick labels will be displayed inside the axis rect and
              ///< clipped to the inner axis rect
    ,
    lsOutside  ///< Tick labels will be displayed outside the axis rect
  };
  Q_ENUMS(LabelSide)
  /*!
      Defines the scale of an axis.
      \see setScaleType
  */
  enum ScaleType {
    stLinear  ///< Linear scaling
    ,
    stLogarithmic  ///< Logarithmic scaling with correspondingly transformed
                   ///< axis coordinates (possibly also \ref setTicker to a \ref
                   ///< BaseChartAxisTickerLog instance).
  };
  Q_ENUMS(ScaleType)
  /*!
    Defines the selectable parts of an axis.
    \see setSelectableParts, setSelectedParts
  */
  enum SelectablePart {
    spNone = 0  ///< None of the selectable parts
    ,
    spAxis = 0x001  ///< The axis backbone and tick marks
    ,
    spTickLabels = 0x002  ///< Tick labels (numbers) of this axis (as a whole,
                          ///< not individually)
    ,
    spAxisLabel = 0x004  ///< The axis label
  };
  Q_ENUMS(SelectablePart)
  Q_FLAGS(SelectableParts)
  Q_DECLARE_FLAGS(SelectableParts, SelectablePart)

  explicit BaseChartAxis(BaseChartAxisRect* parent, AxisType type);
  virtual ~BaseChartAxis() Q_DECL_OVERRIDE;

  // getters:
  AxisType axisType() const { return mAxisType; }
  BaseChartAxisRect* axisRect() const { return mAxisRect; }
  ScaleType scaleType() const { return mScaleType; }
  const BaseChartRange range() const { return mRange; }
  bool rangeReversed() const { return mRangeReversed; }
  double lowerRangePixel() const;
  double upperRangePixel() const;
  double lowerCoord() const;
  double upperCoord() const;
  QSharedPointer<BaseChartAxisTicker> ticker() const { return mTicker; }
  bool ticks() const { return mTicks; }
  bool tickLabels() const { return mTickLabels; }
  int tickLabelPadding() const;
  QFont tickLabelFont() const { return mTickLabelFont; }
  QColor tickLabelColor() const { return mTickLabelColor; }
  double tickLabelRotation() const;
  LabelSide tickLabelSide() const;
  QString numberFormat() const;
  int numberPrecision() const { return mNumberPrecision; }
  QVector<double> tickVector() const { return mTickVector; }
  QVector<QString> tickVectorLabels() const { return mTickVectorLabels; }
  int tickLengthIn() const;
  int tickLengthOut() const;
  bool subTicks() const { return mSubTicks; }
  int subTickLengthIn() const;
  int subTickLengthOut() const;
  QPen basePen() const { return mBasePen; }
  QPen tickPen() const { return mTickPen; }
  QPen subTickPen() const { return mSubTickPen; }
  QFont labelFont() const { return mLabelFont; }
  QColor labelColor() const { return mLabelColor; }
  QString label() const { return mLabel; }
  int labelPadding() const;
  int padding() const { return mPadding; }
  int offset() const;
  SelectableParts selectedParts() const { return mSelectedParts; }
  SelectableParts selectableParts() const { return mSelectableParts; }
  QFont selectedTickLabelFont() const { return mSelectedTickLabelFont; }
  QFont selectedLabelFont() const { return mSelectedLabelFont; }
  QColor selectedTickLabelColor() const { return mSelectedTickLabelColor; }
  QColor selectedLabelColor() const { return mSelectedLabelColor; }
  QPen selectedBasePen() const { return mSelectedBasePen; }
  QPen selectedTickPen() const { return mSelectedTickPen; }
  QPen selectedSubTickPen() const { return mSelectedSubTickPen; }
  BaseChartLineEnding lowerEnding() const;
  BaseChartLineEnding upperEnding() const;
  QBAxisGrid* grid() const { return mGrid; }

  // setters:
  Q_SLOT void setScaleType(BaseChartAxis::ScaleType type);
  Q_SLOT void setRange(const BaseChartRange& range);
  void setRange(double lower, double upper);
  void setRange(double position, double size, Qt::AlignmentFlag alignment);
  void setRangeLower(double lower);
  void setRangeUpper(double upper);
  void setRangeReversed(bool reversed);
  void setTicker(QSharedPointer<BaseChartAxisTicker> ticker);
  void setTicks(bool show);
  void setTickLabels(bool show);
  void setTickLabelPadding(int padding);
  void setTickLabelFont(const QFont& font);
  void setTickLabelColor(const QColor& color);
  void setTickLabelRotation(double degrees);
  void setTickLabelSide(LabelSide side);
  void setNumberFormat(const QString& formatCode);
  void setNumberPrecision(int precision);
  void setTickLength(int inside, int outside = 0);
  void setTickLengthIn(int inside);
  void setTickLengthOut(int outside);
  void setSubTicks(bool show);
  void setSubTickLength(int inside, int outside = 0);
  void setSubTickLengthIn(int inside);
  void setSubTickLengthOut(int outside);
  void setBasePen(const QPen& pen);
  void setTickPen(const QPen& pen);
  void setSubTickPen(const QPen& pen);
  void setLabelFont(const QFont& font);
  void setLabelColor(const QColor& color);
  void setLabel(const QString& str);
  void setLabelPadding(int padding);
  void setPadding(int padding);
  void setOffset(int offset);
  void setSelectedTickLabelFont(const QFont& font);
  void setSelectedLabelFont(const QFont& font);
  void setSelectedTickLabelColor(const QColor& color);
  void setSelectedLabelColor(const QColor& color);
  void setSelectedBasePen(const QPen& pen);
  void setSelectedTickPen(const QPen& pen);
  void setSelectedSubTickPen(const QPen& pen);
  Q_SLOT void setSelectableParts(
      const BaseChartAxis::SelectableParts& selectableParts);
  Q_SLOT void setSelectedParts(
      const BaseChartAxis::SelectableParts& selectedParts);
  void setLowerEnding(const BaseChartLineEnding& ending);
  void setUpperEnding(const BaseChartLineEnding& ending);

  // reimplemented virtual methods:
  virtual double selectTest(const QPointF& pos, bool onlySelectable,
                            QVariant* details = nullptr) const Q_DECL_OVERRIDE;

  // non-property methods:
  Qt::Orientation orientation() const { return mOrientation; }
  int pixelOrientation() const {
    return rangeReversed() != (orientation() == Qt::Vertical) ? -1 : 1;
  }
  void moveRange(double diff);
  void scaleRange(double factor);
  void scaleRange(double factor, double center);
  void setScaleRatio(const BaseChartAxis* otherAxis, double ratio = 1.0);
  void rescale(bool onlyVisiblePlottables = false);
  double pixelToCoord(double value) const;
  double coordToPixel(double value) const;
  SelectablePart getPartAt(const QPointF& pos) const;
  QList<BaseChartAbstractPlottable*> plottables() const;
  QList<BaseChartGraph*> graphs() const;
  QList<BaseChartAbstractItem*> items() const;

  void setYAxis(BaseChartAxis* axis);
  void setCustomGrid();

  static AxisType marginSideToAxisType(MarginSide side);
  static Qt::Orientation orientation(AxisType type) {
    return type == atBottom || type == atTop ? Qt::Horizontal : Qt::Vertical;
  }
  static AxisType opposite(AxisType type);

 signals:
  void rangeChanged(const BaseChartRange& newRange);
  void rangeChanged(const BaseChartRange& newRange,
                    const BaseChartRange& oldRange);
  void scaleTypeChanged(BaseChartAxis::ScaleType scaleType);
  void selectionChanged(const BaseChartAxis::SelectableParts& parts);
  void selectableChanged(const BaseChartAxis::SelectableParts& parts);

 protected:
  // property members:
  // axis base:
  AxisType mAxisType;
  BaseChartAxisRect* mAxisRect;
  // int mOffset; // in QCPAxisPainter
  int mPadding;
  Qt::Orientation mOrientation;
  SelectableParts mSelectableParts, mSelectedParts;
  QPen mBasePen, mSelectedBasePen;
  // BaseChartLineEnding mLowerEnding, mUpperEnding; // in QCPAxisPainter
  //  axis label:
  // int mLabelPadding; // in QCPAxisPainter
  QString mLabel;
  QFont mLabelFont, mSelectedLabelFont;
  QColor mLabelColor, mSelectedLabelColor;
  // tick labels:
  // int mTickLabelPadding; // in QCPAxisPainter
  bool mTickLabels;
  // double mTickLabelRotation; // in QCPAxisPainter
  QFont mTickLabelFont, mSelectedTickLabelFont;
  QColor mTickLabelColor, mSelectedTickLabelColor;
  int mNumberPrecision;
  QLatin1Char mNumberFormatChar;
  bool mNumberBeautifulPowers;
  // bool mNumberMultiplyCross; // QCPAxisPainter
  //  ticks and subticks:
  bool mTicks;
  bool mSubTicks;
  // int mTickLengthIn, mTickLengthOut, mSubTickLengthIn, mSubTickLengthOut; //
  // QCPAxisPainter
  QPen mTickPen, mSelectedTickPen;
  QPen mSubTickPen, mSelectedSubTickPen;
  // scale and range:
  BaseChartRange mRange;
  bool mRangeReversed;
  ScaleType mScaleType;

  // non-property members:
  QBAxisGrid* mGrid;
  BaseChartAxisPainterPrivate* mAxisPainter;
  BaseChartAxis* mYAxis;
  QSharedPointer<BaseChartAxisTicker> mTicker;
  QVector<double> mTickVector;
  QVector<QString> mTickVectorLabels;
  QVector<double> mSubTickVector;
  bool mCachedMarginValid;
  int mCachedMargin;
  bool mDragging;
  BaseChartRange mDragStartRange;
  AntialiasedElements mAADragBackup, mNotAADragBackup;

  // introduced virtual methods:
  virtual int calculateMargin();

  // reimplemented virtual methods:
  virtual void applyDefaultAntialiasingHint(BaseChartPainter* painter) const
      Q_DECL_OVERRIDE;
  virtual void draw(BaseChartPainter* painter) Q_DECL_OVERRIDE;
  virtual Interaction selectionCategory() const Q_DECL_OVERRIDE;
  // events:
  virtual void selectEvent(QMouseEvent* event, bool additive,
                           const QVariant& details,
                           bool* selectionStateChanged) Q_DECL_OVERRIDE;
  virtual void deselectEvent(bool* selectionStateChanged) Q_DECL_OVERRIDE;
  // mouse events:
  virtual void mousePressEvent(QMouseEvent* event,
                               const QVariant& details) Q_DECL_OVERRIDE;
  virtual void mouseMoveEvent(QMouseEvent* event,
                              const QPointF& startPos) Q_DECL_OVERRIDE;
  virtual void mouseReleaseEvent(QMouseEvent* event,
                                 const QPointF& startPos) Q_DECL_OVERRIDE;
  virtual void wheelEvent(QWheelEvent* event) Q_DECL_OVERRIDE;

  // non-virtual methods:
  void setupTickVectors();
  QPen getBasePen() const;
  QPen getTickPen() const;
  QPen getSubTickPen() const;
  QFont getTickLabelFont() const;
  QFont getLabelFont() const;
  QColor getTickLabelColor() const;
  QColor getLabelColor() const;

 private:
  Q_DISABLE_COPY(BaseChartAxis)

  friend class BaseChartCustomPlot;
  friend class QBAxisGrid;
  friend class BaseChartAxisRect;
};
Q_DECLARE_OPERATORS_FOR_FLAGS(BaseChartAxis::SelectableParts)
Q_DECLARE_OPERATORS_FOR_FLAGS(BaseChartAxis::AxisTypes)

class S_WIDGETS_EXPORT BaseChartAxisRect : public BaseChartLayoutElement {
  Q_OBJECT
  /// \cond INCLUDE_QPROPERTIES
  Q_PROPERTY(QPixmap background READ background WRITE setBackground)
  Q_PROPERTY(
      bool backgroundScaled READ backgroundScaled WRITE setBackgroundScaled)
  Q_PROPERTY(Qt::AspectRatioMode backgroundScaledMode READ backgroundScaledMode
                 WRITE setBackgroundScaledMode)
  Q_PROPERTY(Qt::Orientations rangeDrag READ rangeDrag WRITE setRangeDrag)
  Q_PROPERTY(Qt::Orientations rangeZoom READ rangeZoom WRITE setRangeZoom)
  /// \endcond
 public:
  explicit BaseChartAxisRect(BaseChartCustomPlot* parentPlot,
                             bool setupDefaultAxes = true);
  virtual ~BaseChartAxisRect() Q_DECL_OVERRIDE;

  // getters:
  QPixmap background() const { return mBackgroundPixmap; }
  QBrush backgroundBrush() const { return mBackgroundBrush; }
  bool backgroundScaled() const { return mBackgroundScaled; }
  Qt::AspectRatioMode backgroundScaledMode() const {
    return mBackgroundScaledMode;
  }
  Qt::Orientations rangeDrag() const { return mRangeDrag; }
  Qt::Orientations rangeZoom() const { return mRangeZoom; }
  BaseChartAxis* rangeDragAxis(Qt::Orientation orientation);
  BaseChartAxis* rangeZoomAxis(Qt::Orientation orientation);
  QList<BaseChartAxis*> rangeDragAxes(Qt::Orientation orientation);
  QList<BaseChartAxis*> rangeZoomAxes(Qt::Orientation orientation);
  double rangeZoomFactor(Qt::Orientation orientation);

  // setters:
  void setBackground(const QPixmap& pm);
  void setBackground(const QPixmap& pm, bool scaled,
                     Qt::AspectRatioMode mode = Qt::KeepAspectRatioByExpanding);
  void setBackground(const QBrush& brush);
  void setBackgroundScaled(bool scaled);
  void setBackgroundScaledMode(Qt::AspectRatioMode mode);
  void setRangeDrag(Qt::Orientations orientations);
  void setRangeZoom(Qt::Orientations orientations);
  void setRangeDragAxes(BaseChartAxis* horizontal, BaseChartAxis* vertical);
  void setRangeDragAxes(QList<BaseChartAxis*> axes);
  void setRangeDragAxes(QList<BaseChartAxis*> horizontal,
                        QList<BaseChartAxis*> vertical);
  void setRangeZoomAxes(BaseChartAxis* horizontal, BaseChartAxis* vertical);
  void setRangeZoomAxes(QList<BaseChartAxis*> axes);
  void setRangeZoomAxes(QList<BaseChartAxis*> horizontal,
                        QList<BaseChartAxis*> vertical);
  void setRangeZoomFactor(double horizontalFactor, double verticalFactor);
  void setRangeZoomFactor(double factor);

  // non-property methods:
  int axisCount(BaseChartAxis::AxisType type) const;
  BaseChartAxis* axis(BaseChartAxis::AxisType type, int index = 0) const;
  QList<BaseChartAxis*> axes(BaseChartAxis::AxisTypes types) const;
  QList<BaseChartAxis*> axes() const;
  BaseChartAxis* addAxis(BaseChartAxis::AxisType type,
                         BaseChartAxis* axis = nullptr);
  QList<BaseChartAxis*> addAxes(BaseChartAxis::AxisTypes types);
  bool removeAxis(BaseChartAxis* axis);
  BaseChartLayoutInset* insetLayout() const { return mInsetLayout; }

  void zoom(const QRectF& pixelRect);
  void zoom(const QRectF& pixelRect, const QList<BaseChartAxis*>& affectedAxes);
  void setupFullAxesBox(bool connectRanges = false);
  QList<BaseChartAbstractPlottable*> plottables() const;
  QList<BaseChartGraph*> graphs() const;
  QList<BaseChartAbstractItem*> items() const;

  // read-only interface imitating a QRect:
  int left() const { return mRect.left(); }
  int right() const { return mRect.right(); }
  int top() const { return mRect.top(); }
  int bottom() const { return mRect.bottom(); }
  int width() const { return mRect.width(); }
  int height() const { return mRect.height(); }
  QSize size() const { return mRect.size(); }
  QPoint topLeft() const { return mRect.topLeft(); }
  QPoint topRight() const { return mRect.topRight(); }
  QPoint bottomLeft() const { return mRect.bottomLeft(); }
  QPoint bottomRight() const { return mRect.bottomRight(); }
  QPoint center() const { return mRect.center(); }

  // reimplemented virtual methods:
  virtual void update(UpdatePhase phase) Q_DECL_OVERRIDE;
  virtual QList<BaseChartLayoutElement*> elements(bool recursive) const
      Q_DECL_OVERRIDE;

 protected:
  // property members:
  QBrush mBackgroundBrush;
  QPixmap mBackgroundPixmap;
  QPixmap mScaledBackgroundPixmap;
  bool mBackgroundScaled;
  Qt::AspectRatioMode mBackgroundScaledMode;
  BaseChartLayoutInset* mInsetLayout;
  Qt::Orientations mRangeDrag, mRangeZoom;
  QList<QPointer<BaseChartAxis> > mRangeDragHorzAxis, mRangeDragVertAxis;
  QList<QPointer<BaseChartAxis> > mRangeZoomHorzAxis, mRangeZoomVertAxis;
  double mRangeZoomFactorHorz, mRangeZoomFactorVert;

  // non-property members:
  QList<BaseChartRange> mDragStartHorzRange, mDragStartVertRange;
  AntialiasedElements mAADragBackup, mNotAADragBackup;
  bool mDragging;
  QHash<BaseChartAxis::AxisType, QList<BaseChartAxis*> > mAxes;

  // reimplemented virtual methods:
  virtual void applyDefaultAntialiasingHint(BaseChartPainter* painter) const
      Q_DECL_OVERRIDE;
  virtual void draw(BaseChartPainter* painter) Q_DECL_OVERRIDE;
  virtual int calculateAutoMargin(MarginSide side) Q_DECL_OVERRIDE;
  virtual void layoutChanged() Q_DECL_OVERRIDE;
  // events:
  virtual void mousePressEvent(QMouseEvent* event,
                               const QVariant& details) Q_DECL_OVERRIDE;
  virtual void mouseMoveEvent(QMouseEvent* event,
                              const QPointF& startPos) Q_DECL_OVERRIDE;
  virtual void mouseReleaseEvent(QMouseEvent* event,
                                 const QPointF& startPos) Q_DECL_OVERRIDE;
  virtual void wheelEvent(QWheelEvent* event) Q_DECL_OVERRIDE;

  // non-property methods:
  void drawBackground(BaseChartPainter* painter);
  void updateAxesOffset(BaseChartAxis::AxisType type);

 private:
  Q_DISABLE_COPY(BaseChartAxisRect)

  friend class BaseChartCustomPlot;
};

class BaseChartAxisPainterPrivate {
 public:
  explicit BaseChartAxisPainterPrivate(BaseChartCustomPlot* parentPlot);
  virtual ~BaseChartAxisPainterPrivate();

  virtual void draw(BaseChartPainter* painter);
  virtual int size();
  void clearCache();
  void setCustomGrid();

  QRect axisSelectionBox() const { return mAxisSelectionBox; }
  QRect tickLabelsSelectionBox() const { return mTickLabelsSelectionBox; }
  QRect labelSelectionBox() const { return mLabelSelectionBox; }
  bool isCustomGrid() const { return customGrid; }

  // public property members:
  BaseChartAxis::AxisType type;
  QPen basePen;
  BaseChartLineEnding lowerEnding,
      upperEnding;   // directly accessed by BaseChartAxis setters/getters
  int labelPadding;  // directly accessed by BaseChartAxis setters/getters
  QFont labelFont;
  QColor labelColor;
  QString label;
  int tickLabelPadding;  // directly accessed by BaseChartAxis setters/getters
  double
      tickLabelRotation;  // directly accessed by BaseChartAxis setters/getters
  BaseChartAxis::LabelSide
      tickLabelSide;  // directly accessed by BaseChartAxis setters/getters
  bool substituteExponent;
  bool numberMultiplyCross;  // directly accessed by BaseChartAxis
                             // setters/getters
  int tickLengthIn, tickLengthOut, subTickLengthIn,
      subTickLengthOut;  // directly accessed by BaseChartAxis setters/getters
  QPen tickPen, subTickPen;
  QFont tickLabelFont;
  QColor tickLabelColor;
  QRect axisRect, viewportRect;
  int offset;  // directly accessed by BaseChartAxis setters/getters
  bool abbreviateDecimalPowers;
  bool reversedEndings;
  bool customGrid;

  QVector<double> subTickPositions;
  QVector<double> tickPositions;
  QVector<QString> tickLabels;

 protected:
  struct CachedLabel {
    QPointF offset;
    QPixmap pixmap;
  };
  struct TickLabelData {
    QString basePart, expPart, suffixPart;
    QRect baseBounds, expBounds, suffixBounds, totalBounds, rotatedTotalBounds;
    QFont baseFont, expFont;
  };
  BaseChartCustomPlot* mParentPlot;
  QByteArray mLabelParameterHash;  // to determine whether mLabelCache needs to
                                   // be cleared due to changed parameters
  QCache<QString, CachedLabel> mLabelCache;
  QRect mAxisSelectionBox, mTickLabelsSelectionBox, mLabelSelectionBox;

  virtual QByteArray generateLabelParameterHash() const;

  virtual void placeTickLabel(BaseChartPainter* painter, double position,
                              int distanceToAxis, const QString& text,
                              QSize* tickLabelsSize);
  virtual void drawTickLabel(BaseChartPainter* painter, double x, double y,
                             const TickLabelData& labelData) const;
  virtual TickLabelData getTickLabelData(const QFont& font,
                                         const QString& text) const;
  virtual QPointF getTickLabelDrawOffset(const TickLabelData& labelData) const;
  virtual void getMaxTickLabelSize(const QFont& font, const QString& text,
                                   QSize* tickLabelsSize) const;
};

Q_DECLARE_METATYPE(BaseChartAxisTicker::TickStepStrategy)
Q_DECLARE_METATYPE(QSharedPointer<BaseChartAxisTicker>)
Q_DECLARE_METATYPE(BaseChartAxisTickerTime::TimeUnit)
Q_DECLARE_METATYPE(BaseChartAxisTickerFixed::ScaleStrategy)
Q_DECLARE_METATYPE(BaseChartAxisTickerPi::FractionStyle)
Q_DECLARE_METATYPE(BaseChartAxis::AxisType)
Q_DECLARE_METATYPE(BaseChartAxis::LabelSide)
Q_DECLARE_METATYPE(BaseChartAxis::ScaleType)
Q_DECLARE_METATYPE(BaseChartAxis::SelectablePart)
